package com.magenta.maxoptra.dto;

/**
 * Project: MaxOptra
 * User: kaganov
 * Date: 19.05.2009 19:08:28
 * <p/>
 * Copyright (c) 1999-2007 Magenta Corporation Ltd. All Rights Reserved.
 * Magenta Technology proprietary and confidential.
 * Use is subject to license terms.
 */

import com.magenta.maxoptra.utils.Point;

import java.io.Serializable;
import java.util.*;

public class PolygonCache implements Serializable {
    private final Map<Integer, Set<SpatialSegment>> segments;
    private final Double minLongitude;
    private final Double maxLongitude;
    private final Double minLatitude;
    private final Double maxLatitude;

    public PolygonCache(Polygon polygon) {
        if (polygon == null) {
            throw new IllegalArgumentException("Polygon is null");
        }
        Point[] points = polygon.getPoints();
        if (points == null || points.length <= 2) {
            throw new IllegalArgumentException("Polygon has null points or only one point");
        }
        this.maxLatitude = polygon.getMaxLatitude();
        this.maxLongitude = polygon.getMaxLongitude();
        this.minLatitude = polygon.getMinLatitude();
        this.minLongitude = polygon.getMinLongitude();

        Map<Integer, Set<SpatialSegment>> segments = new HashMap<Integer, Set<SpatialSegment>>(points.length * 10);

        int j = points.length - 1;
        for (int i = 0; i < points.length; i++) {
            final Point point1 = points[i];
            final Point point2 = points[j];
            final SpatialSegment spatialSegment = point1.getLatitude() < point2.getLatitude()
                    ? new SpatialSegment(point1, point2)
                    : new SpatialSegment(point2, point1);
            int intLatitude1 = (int) Math.floor(spatialSegment.getLatitude1());
            int intLatitude2 = (int) Math.ceil(spatialSegment.getLatitude2());
            for (int k = intLatitude1; k <= intLatitude2; k++) {
                Set<SpatialSegment> spatialSegments = segments.get(k);
                if (spatialSegments == null) {
                    spatialSegments = new HashSet<SpatialSegment>();
                    segments.put(k, spatialSegments);
                }
                spatialSegments.add(spatialSegment);
            }

            j = i;
        }
        this.segments = Collections.unmodifiableMap(segments);
    }

    public Set<SpatialSegment> getSpatialSegments(double latitude) {
        final Set<SpatialSegment> spatialSegments1 = segments.get((int) Math.floor(latitude));
        final Set<SpatialSegment> spatialSegments2 = segments.get((int) Math.ceil(latitude));

        final Set<SpatialSegment> spatialSegments = new HashSet<SpatialSegment>();
        if (spatialSegments1 != null) {
            spatialSegments.addAll(spatialSegments1);
        }
        if (spatialSegments2 != null) {
            spatialSegments.addAll(spatialSegments2);
        }
        removeUncrossedSegments(latitude, spatialSegments);
        return Collections.unmodifiableSet(spatialSegments);
    }

    public Double getMinLongitude() {
        return minLongitude;
    }

    public Double getMaxLongitude() {
        return maxLongitude;
    }

    public Double getMinLatitude() {
        return minLatitude;
    }

    public Double getMaxLatitude() {
        return maxLatitude;
    }

    private void removeUncrossedSegments(double latitude, Set<SpatialSegment> spatialSegments) {
        Iterator<SpatialSegment> it = spatialSegments.iterator();
        while (it.hasNext()) {
            SpatialSegment spatialSegment = it.next();
            if (!((spatialSegment.getLatitude1() <= latitude) && (latitude <= spatialSegment.getLatitude2()))) {
                it.remove();
            }
        }
    }

    public void clear() {
        for (Integer i : segments.keySet()) {
            Set<SpatialSegment> spatialSegments = segments.get(i);
            if (spatialSegments != null) {
                spatialSegments.clear();
            }
        }
    }
}